package org.hfy.calc;

import dnl.utils.text.table.TextTable;
import org.hfy.Constants;
import org.hfy.collections.LRAutomaton;
import org.hfy.collections.SLRAutomaton;
import org.hfy.collections.helper.*;

import java.util.*;

/**
 * @Description:
 * @Author: Takagi
 * @date: 2023/5/27
 */
public class Calculator {
    public LRAutomaton lrAutomaton;
    public SymbolString inputString;

    public Calculator(Symbol start, List<Production> productionList, SymbolString string) {
        this.lrAutomaton = new SLRAutomaton();
        this.lrAutomaton.init(start, productionList);
        this.inputString = string;
    }

    /*
     * @Author Takagi
     * @Description: 处理 val 栈的归约
     * @date: 2023/5/28
     * @param valStack
     * @param action
     * @return
     **/
    public void handleValueStackReduce(Stack<Symbol> valStack, Action action) {
        Production production = action.production;
        SymbolString right = production.getRightIgnoreItem();
        if (right.size() == 3) {
            if (production.left.equals(right.getFirstSymbol())) {
                Symbol op = right.symbolList.get(1);
                Symbol a = valStack.pop();
                valStack.pop();
                Symbol b = valStack.pop();
                if (op.equals("+")) {
                    Symbol symbol = new Symbol(String.valueOf(a.value + b.value));
                    symbol.value = a.value + b.value;
                    valStack.add(symbol);
                } else if (op.equals("*")) {
                    Symbol symbol = new Symbol(String.valueOf(a.value * b.value));
                    symbol.value = a.value * b.value;
                    valStack.add(symbol);
                }
            } else {
                valStack.pop();
                Symbol mid = valStack.pop();
                valStack.pop();
                Symbol symbol = new Symbol(String.valueOf(mid));
                symbol.value = mid.value;
                valStack.add(symbol);
            }
        }
    }

    /*
     * @Author Takagi
     * @Description: 综合属性计算
     * @date: 2023/5/27
     * @param
     * @return
     **/
    public void synthesizedAttributeCalc(Queue<Integer> lexvalList) {
        inputString.addSymbol(new Symbol(Constants.EOF));
        LRPredictTable table = this.lrAutomaton.constructLrPredictTable();
        String[] header = {"stack", "val", "input", "action"};
        Stack<Symbol> stateStack = new Stack<>();
        Stack<Symbol> valStack = new Stack<>();
        Queue<Symbol> queue = new LinkedList<>(inputString.symbolList);
        List<List<String>> data = new LinkedList<>();

        stateStack.add(table.firstStateName());

        while (stateStack.size() > 0) {
            Symbol stateName = stateStack.peek();
            Symbol inputTop = queue.peek();
            State stateTop = table.getState(stateName);
            Action action = stateTop.getAction(inputTop);

            List<String> line = new LinkedList<>();
            line.add(stateStack.toString());
            line.add(valStack.toString());
            line.add(queue.toString());

//            System.out.println(stateStack);
//            System.out.println(valStack);
//            System.out.println("\n");

            if (action == null) {
                // 无对应动作
                line.add(Constants.REJECT);
                data.add(line);
                break;
            } else if (action.accept) {
                // 接受
                line.add(Constants.ACCEPT);
                data.add(line);
                break;
            } else {
                if (Constants.isShiftAction(action.actionType)) {
                    // 移进状态
                    // 如果是 digit，那么从词法分析器取值
                    if (inputTop.equals("digit")) {
                        inputTop.value = lexvalList.poll();
                    }
                    // 弹出输入队头
                    queue.poll();
                    // si
                    stateStack.add(inputTop);
                    stateStack.add(action.stateNo);
                    //
                    valStack.add(inputTop);
                    line.add(Constants.SHIFT_ACTION);
                } else {
                    // 归约状态

                    // 移除状态栈中 2 倍的产生式右侧符号数量
                    int size = action.production.getProductionIgnoreItem().right.size();
                    for (int i = 0; i < size * 2; i++) {
                        stateStack.pop();
                    }
                    // 无对应转移状态，则拒绝输入串
                    State topState = table.getState(stateStack.peek());
                    stateStack.add(action.getReducedSymbol());
                    if (topState.getGoto(action.getReducedSymbol()) == null) {
                        line.add(Constants.REJECT);
                        data.add(line);
                        break;
                    } else {
                        // 将转移后的状态入栈
                        stateStack.add(topState.getGoto(action.getReducedSymbol()));
                        // 处理 value 栈
                        handleValueStackReduce(valStack, action);
                        line.add(action.production.getProductionIgnoreItem().toString());
                    }
                }
            }
            data.add(line);
//            System.out.println(line);
        }
        // 打印预测过程
        TextTable textTable = new TextTable(header, data.stream()
                .map(l -> l.toArray(new String[0]))
                .toArray(String[][]::new));
        textTable.printTable();
    }

    /*
     * @Author Takagi
     * @Description: 构建 LL(1) 分析表
     * @date: 2023/5/28
     * @param
     * @return * @return LLTable
     **/
    public LLTable constructLLTable() {
        LLTable table = new LLTable();
        // 遍历每一条产生式 A -> a
        for (Production production : lrAutomaton.productions) {
            Symbol left = production.left;
            SymbolString right = production.right;
            LLState state = table.get(left);
            if (Constants.isEpsilon(right)) {
                // a = epsilon
                state.addAll(lrAutomaton.getNonTerminatorByName(left).Follow, production);
            } else {
                // a != epsilon
                Symbol first = right.getFirstSymbol();
                if (Constants.isTerminator(first)) {
                    // a 是终结符，添加 A -> a 到 a 列
                    state.add(first, production);
                } else {
                    // a 是非终结符，添加 A -> a 到 First(a)
                    state.addAll(lrAutomaton.getNonTerminatorByName(first).First, production);
                }
            }
            table.add(left, state);
        }
        return table;
    }

    /*
     * @Author Takagi
     * @Description: 分离每个动作中的符号
     * @date: 2023/5/28
     * @param symbol
     * @return * @return String[]
     **/
    public String[] parseActionSymbol(Symbol symbol) {
        return symbol.content.split("\\.");
    }

    public String attrStackToString(List<Symbol> attrStack) {
        StringBuilder sb = new StringBuilder();
        for (Symbol symbol : attrStack) {
            sb.append(String.format("[%s]\n", symbol.content));
            symbol.attr.forEach((k, v) -> {
                sb.append(String.format("%s : %s\t", k, v));
                sb.append("\n");
            });
        }
        return sb.toString();
    }

    /*
     * @Author Takagi
     * @Description: 处理翻译动作
     * @date: 2023/5/28
     * @param analysisStack
     * @param attrStack
     * @return * @return String
     **/
    public String handleTranslate(Stack<SymbolString> analysisStack, List<Symbol> attrStack) {
        SymbolString action = analysisStack.pop();
        if (action.size() - 2 == 3) {
            // 二元赋值语句
            // F.val = digit.lexval
            Symbol a = action.symbolList.get(1);
            Symbol b = action.symbolList.get(3);
            String aName = parseActionSymbol(a)[0];
            String aField = parseActionSymbol(a)[1];
            String bName = parseActionSymbol(b)[0];
            String bField = parseActionSymbol(b)[1];
            // 从 attr 栈中找到 b
            Symbol bInStack = attrStack.get(attrStack.indexOf(new Symbol(bName)));
            Symbol tmp = new Symbol(aName);
            int val = bInStack.attr.get(bField);
            tmp.attr.put(aField, val);
            // 用过 b 的属性了，自上而下中，可以丢弃
            bInStack.attr.remove(bField);
            if (bInStack.attr.size() == 0) attrStack.remove(bInStack);
            attrStack.add(tmp);
            return String.format("%s ==> %d", action, val);
        } else if (action.size() - 2 == 5) {
            // 三元赋值语句
            // { E.val = ( E1.val ) }
            // { R1.i = R.i + T.val }
            Symbol a = action.symbolList.get(1);
            String aName = parseActionSymbol(a)[0];
            Symbol b = action.symbolList.get(3);
            String bName = parseActionSymbol(b)[0];
            if (!Constants.isBracket(b)) {
                Symbol op = action.symbolList.get(4);
                Symbol c = action.symbolList.get(5);
                String cName = parseActionSymbol(c)[0];
                String aField = parseActionSymbol(a)[1];
                String bField = parseActionSymbol(b)[1];
                String cField = parseActionSymbol(c)[1];
                Symbol bInStack = attrStack.get(attrStack.indexOf(new Symbol(bName)));
                Symbol cInStack = attrStack.get(attrStack.indexOf(new Symbol(cName)));

                Symbol tmp = new Symbol(aName);
                int val = 0;
                if (op.equals("+")) {
                    val = bInStack.attr.get(bField) + cInStack.attr.get(cField);
                } else if (op.equals("*")) {
                    val = bInStack.attr.get(bField) * cInStack.attr.get(cField);
                }
                bInStack.attr.remove(bField);
                if (bInStack.attr.size() == 0) attrStack.remove(bInStack);
                cInStack.attr.remove(cField);
                if (cInStack.attr.size() == 0) attrStack.remove(cInStack);
                tmp.attr.put(aField, val);
                attrStack.add(tmp);
                return String.format("%s ==> %d", action, val);
            }
            String aField = parseActionSymbol(a)[1];
            Symbol aInStack = attrStack.get(attrStack.indexOf(new Symbol(aName)));
            Integer val = aInStack.attr.get(aField);
            return String.format("%s ==> %d", action, val);
        } else {
            // { print( E.val ) }
            Symbol a = action.symbolList.get(3);
            String aName = parseActionSymbol(a)[0];
            String aField = parseActionSymbol(a)[1];
            Symbol aInStack = attrStack.get(attrStack.indexOf(new Symbol(aName)));
            Integer val = aInStack.attr.get(aField);
            return String.format("%s ==> %d", action, val);
        }
    }

    /*
     * @Author Takagi
     * @Description: L属性自上而下翻译
     * @date: 2023/5/28
     * @param lexvalList
     * @param productionTranslate
     * @return
     **/
    public void inheritanceAttributeCalc(Queue<Integer> lexvalList, Map<Production, List<SymbolString>> productionTranslate) {
        inputString.addSymbol(new Symbol(Constants.EOF));
        LLTable table = constructLLTable();
        Stack<SymbolString> analysisStack = new Stack<>();
        List<Symbol> attrStack = new LinkedList<>();
        Queue<Symbol> queue = new LinkedList<>(inputString.symbolList);
        analysisStack.add(new SymbolString(Constants.EOF));
        analysisStack.add(new SymbolString(lrAutomaton.start.getContent()));

        String[] header = {"stack", "input", "action"};
        List<List<String>> data = new LinkedList<>();

        while (analysisStack.size() > 0) {
//            System.out.println(analysisStack);
            List<String> line = new LinkedList<>();
            line.add(analysisStack.toString());
            line.add(queue.toString());

            SymbolString analysisTop = analysisStack.peek();
            if (analysisTop.size() > 1) {
                // 动作
                String action = handleTranslate(analysisStack, attrStack);
                line.add(action);
            } else {
                // 符号
                Symbol inputTop = queue.peek();
                LLState state = table.get(analysisTop.getFirstSymbol());
                if (Constants.isTerminator(analysisTop.getFirstSymbol())) {
                    // 栈顶是终结符
                    if (Constants.isEpsilon(analysisTop.getFirstSymbol())) {
                        // 栈顶是 epsilon
                        analysisStack.pop();
                        continue;
                    }
                    if (analysisTop.equals(inputTop)) {
                        // 匹配串
                        if (analysisTop.getFirstSymbol().equals("digit")) {
                            // 如果是 digit，匹配成功时要加入属性栈
                            Symbol tmp = new Symbol(analysisTop.getFirstSymbol().content);
                            tmp.attr.put("lexval", lexvalList.poll());
                            attrStack.add(tmp);
                        }
                        line.add("match" + inputTop);
                        analysisStack.pop();
                        queue.poll();
                    } else {
                        // 栈顶终结符与输入符号不同
                        line.add("reject");
                        data.add(line);
                        break;
                    }
                } else {
                    // 栈顶是非终结符
                    Production production = state.get(inputTop);
                    analysisStack.pop();
                    // 产生式对应的翻译方案
                    List<SymbolString> string = productionTranslate.get(production);
                    if (production == null) {
                        // 没有对应产生式，拒绝串
                        line.add("reject");
                        data.add(line);
                        break;
                    }
                    // 翻译方案逆序入栈
                    for (int i = string.size() - 1; i >= 0; --i) {
                        analysisStack.add(string.get(i));
                    }
                }
            }

            data.add(line);
        }

        // 打印预测过程
        TextTable textTable = new TextTable(header, data.stream()
                .map(l -> l.toArray(new String[0]))
                .toArray(String[][]::new));
        textTable.printTable();
    }
}
